FCNTL(2) | Linux Programmer's Manual | FCNTL(2) |
名前¶
fcntl - ファイルディスクリプタの操作を行う
書式¶
#include <unistd.h> #include <fcntl.h> int fcntl(int fd, int cmd, ... /* arg */ );
説明¶
fcntl() は、オープンされたファイルディスクリプタ fd に関して下記の操作を行う。操作は cmd によって決まる:
fcntl() はオプションとして第三引き数をとることができる。 第三引き数が必要かどうかは cmd により決まる。 必要な引き数の型は cmd 名の後ろの括弧内で指定されている (ほとんどの場合、必要な型は long であり、この引き数を表すのに arg という名前を使っている)。 引き数が必要ない場合には void が指定されている。
ファイルディスクリプタの複製¶
- F_DUPFD (long)
- 利用可能なファイルディスクリプタのうち、 arg 以上で最小のものを探し、 fd のコピーとする。これは別の形の dup2(2) である。 dup2(2) では指定されたディスクリプタが使われる点が違う。
- 成功すると、新しいディスクリプタが返される。
- 詳細は dup(2) を参照のこと。
- F_DUPFD_CLOEXEC (long; Linux 2.6.24 以降)
- F_DUPFD と同様だが、それに加えて複製されたディスクリプタに対して close-on-exec フラグをセットする。 このフラグを指定することで、プログラムは FD_CLOEXEC フラグをセットするために fcntl() の F_SETFD 操作を追加で行う必要がなくなる。 このフラグがなぜ有用かについては、 open(2) の O_CLOEXEC の説明を参照のこと。
ファイルディスクリプタ・フラグ¶
以下のコマンドを使って、ファイルディスクリプタに関連するフラグ を操作することができる。 現在のところ、定義されているフラグは一つだけである: FD_CLOEXEC (close-on-exec フラグ)。 FD_CLOEXEC ビットが 0 なら、ファイルディスクリプタは execve(2) を行ってもオープンされたままだが、そうでない場合はクローズされる。
- F_GETFD (void)
- ファイルディスクリプタ・フラグを読み出す。 arg は無視される。
- F_SETFD (long)
- ファイルディスクリプタ・フラグに arg で指定した値を設定する。
ファイル状態フラグ¶
オープンファイル記述 (open file description) には、 ファイル記述毎に設定される状態フラグがいくつかある。これらのフラグは open(2) によって初期化され、 fcntl(2) により変更することもできる。これらは、 (dup(2), fcntl(F_DUPFD), fork(2) などで) 複製されたファイルディスクリプタ同士は 同じオープンファイル記述を参照する。 そのため、 同じファイル状態フラグが共有される。
ファイル状態フラグとその意味は open(2) で説明されている。
- F_GETFL (void)
- ファイル状態フラグを読み出す。 arg は無視される。
- F_SETFL (long)
- ファイル状態フラグに arg で指定された値を設定する。 arg のうち、ファイルのアクセスモード (O_RDONLY, O_WRONLY, O_RDWR) とファイル作成フラグ (すなわち O_CREAT, O_EXCL, O_NOCTTY, O_TRUNC) に関するビットは無視される。 Linux では、このコマンドで変更できるのは O_APPEND, O_ASYNC, O_DIRECT, O_NOATIME, O_NONBLOCK フラグだけである。
アドバイザリ・ロック¶
F_GETLK, F_SETLK, F_SETLKW
は、レコード・ロックの獲得/解放/テストのために使用する
(レコード・ロックはファイルセグメント・ロックや
ファイル領域ロックとも呼ばれる)。
三番目の引き数 lock
は、以下に示すフィールドを含む構造体へのポインタである
(フィールドの順序は関係なく、構造体に他のフィールドがあってもよい)。
struct flock {
...
short l_type; /* Type of lock: F_RDLCK,
F_WRLCK, F_UNLCK */
short l_whence; /* How to interpret l_start:
SEEK_SET, SEEK_CUR, SEEK_END */
off_t l_start; /* Starting offset for lock */
off_t l_len; /* Number of bytes to lock */
pid_t l_pid; /* PID of process blocking our lock
(F_GETLK only) */
... };
この構造体の l_whence, l_start, l_len フィールドで、ロックを行いたいバイト範囲を指定する。 ファイルの末尾より後ろのバイトをロックすることはできるが、 ファイルの先頭より前のバイトをロックすることはできない。
l_start はロックを行う領域の開始オフセットである。 その意味は l_whence により異なる: l_whence が SEEK_SET の場合はファイルの先頭からのオフセット、 l_whence が SEEK_CUR の場合は現在のファイルオフセットからのオフセット、 l_whence が SEEK_END の場合はファイルの末尾からのオフセットと解釈される。 後ろの2つの場合には、 ファイルの先頭より前にならない範囲で、 l_start に負の値を指定することができる。
l_len はロックしたいバイト数を示す。 l_len が正の場合、ロックされるバイト範囲は l_start 以上 l_start+l_len-1 以下となる。 l_len に 0 を指定した場合は特別な意味を持つ: l_whence and l_start で指定される位置からファイルの末尾までの全てのバイトをロックする (ファイルがどんなに大きくなったとしてもファイルの末尾までロックする)。
POSIX.1-2001 では、負の値の l_len をサポートする実装を認めている (必須ではない)。 l_len が負の場合、ロックされるバイト範囲は l_start+l_len 以上 l_start-1 以下となる。 この動作はカーネル 2.4.21 以降および 2.5.49 以降の Linux で サポートされている。
l_type フィールドは、ファイルに対して読み出しロック (F_RDLCK) と書き込みロック (F_WRLCK) のどちらを 設定するかを指定する。 ファイルのある領域に対して、読み出しロック (共有ロック) を保持できる プロセス数に制限はないが、書き込みロック (排他ロック) を保持できる のは一つのプロセスだけである。排他ロックを設定すると、(共有ロックか 排他ロックにかかわらず) 他のロックは何も設定できない。 一つのプロセスは、ファイルのある領域に対して一種類のロックしか保持できない。 新規のロックがロックが設定されている領域に対して適用されると、既存のロック は新規のロックの種別に変換される (新規のロックで指定されたバイト範囲が既存ロックの範囲と一致する場合以外では、 変換の過程で既存のロックの分割、縮小、結合が行われることがある)。
- F_SETLK (struct flock *)
- (l_type が F_RDLCK か F_WRLCK の場合は) ロックの獲得を、 (F_UNLCK の場合は) ロックの解放を、 flock 構造体のフィールド l_whence, l_start, l_len で指定された範囲のバイトに対して行う。 指定されたロックが他のプロセスが設定しているロックと衝突する場合は、 -1 を返し、 errno に EACCES か EAGAIN を設定する。
- F_SETLKW (struct flock *)
- F_SETLK と同様だが、こちらではそのファイルに対して衝突するロックが 適用されていた場合に、そのロックが解放されるのを待つ点が異なる。 待っている間にシグナルを受けた場合は、システムコールは中断され、 (シグナルハンドラが戻った直後に) 返り値 -1 を返す (また errno に EINTR が設定される; signal(7) 参照)。
- F_GETLK (struct flock *)
- このコールの呼び出し時には、 lock にはそのファイルに適用しようとするロックに関する情報が入っている。 ロックを適用できる場合には、 fcntl() は実際にはロックを行わず、構造体 lock の l_type フィールドに F_UNLCK を設定し、他のフィールドは変更せずに、復帰する。 違う種別のロックが (一つもしくは複数) 適用されていて ロックを適用できないような場合には、 fcntl() は、原因となったロックの一つについての詳細情報を構造体 lock のフィールド l_type, l_whence, l_start, l_len に格納し、また l_pid にロックを保持しているプロセスの PID を設定して、復帰する。
読み出しロックを適用するには、 fd は読み出し用にオープンされていなければならない。 書き込みロックを適用するには、 fd は書き込み用にオープンされていなければならない。 読み書き両方のロックを適用するには、読み書き両用で ファイルをオープンしなければならない。
レコードのロックは、 F_UNLCK により明示的に削除されるだけでなく、 プロセスが終了したときや、ロックが適用されているファイルを参照している ファイルディスクリプタのいずれかがクローズされた場合にも解放される。 このロックの解放は自動的に行われる。 この動作はまずい: あるプロセスが /etc/passwd や /etc/mtab といったファイルにロックを適用しているときに、 あるライブラリ関数が何かの理由で同じファイルを open, read, close すると、そのファイルへのロックが失われることになる。
レコードのロックは fork(2) で作成された子プロセスには継承されないが、 execve(2) の前後では保存される。
stdio(3) ではバッファリングが行われるので、 stdio 関連の関数ではレコードのロックの使用は回避される; 代わりに read(2) や write(2) を使用すること。
強制ロック (mandatory locking)¶
上述のロックにはアドバイザリ・ロック (advisory lock) と強制ロック (mandatory lock) の二種類があるが、デフォルトではアドバイザリ・ロックとなる。
アドバイザリ・ロックに強制力はなく、協調して動作するプロセス間でのみ 有効である。
強制ロックは全てのプロセスに対して効果がある。 あるプロセスが互換性のない強制ロックが適用されたファイル領域に対して (read(2) や write(2) により) 互換性のないアクセスを実行しようとした場合、 アクセスの結果は そのファイルのオープンファイル記述で O_NONBLOCK フラグが有効になっているかにより決まる。 O_NONBLOCK フラグが有効になっていないときは、ロックが削除されるか、 ロックがアクセスと互換性のあるモードに変換されるまで、 システムコールは停止 (block) される。 O_NONBLOCK フラグが有効になっているときは、システムコールはエラー EAGAIN で失敗する。
強制ロックを使用するためには、ロック対象のファイルが含まれるファイルシステム と、ロック対象のファイル自身の両方について、強制ロックが有効になっていなけれ ばならない。ファイルシステムについて強制ロックを有効にするには、 mount(8) に "-o mand" オプションを渡すか、 mount(2) に MS_MANDLOCK フラグを指定する。ファイルについて強制ロックを有効にするには、 そのファイルのグループ実行許可 (group execute permission) を無効とし、 かつ set-group-ID 許可ビットを有効にする (chmod(1) と chmod(2) を参照)。
Linux の強制ロックの実装は信頼性に欠けるものである。 下記の「バグ」の節を参照のこと。
シグナルの管理¶
F_GETOWN, F_SETOWN, F_GETSIG, F_SETSIG は、I/O が利用可能になったことを示すシグナルを管理するために使用する。
- F_GETOWN (void)
- ファイルディスクリプタ fd のイベントに対するシグナル SIGIO および SIGURG を受けているプロセスのプロセスID かプロセスグループを (関数の結果として) 返す。 プロセスID は正の値として返される。 プロセスグループID は負の値として返される (下記のバグの章を参照)。 arg は無視される。
- F_SETOWN (long)
- ファイルディスクリプタ
fd
のイベント発生を知らせるシグナル
SIGIO や SIGURG
を受けるプロセスの
プロセス ID
またはプロセスグループID
を arg で指定された ID
に設定する。
プロセスID
は正の値として指定し、
プロセスグループID
は負の値として指定する。
ほとんどの場合、呼び出し元プロセスは所有者として自分自身を指定する
(つまり arg に getpid(2)
を指定する)。
fcntl() の F_SETFL コマンドを使用してファイルディスクリプタに O_ASYNC 状態フラグを設定した場合には、そのファイルディスクリプタへの 入出力が可能になる度に SIGIO シグナルが送られる。 F_SETSIG は SIGIO 以外の別のシグナルの配送を受けられるように するのにも使うことができる。 許可 (permission) のチェックで失敗した場合には、 シグナルは黙って捨てられる。
F_SETOWN により指定された所有者のプロセス (またはプロセスグループ) に シグナルを送る際には、 kill(2) に書かれているのと同じ許可のチェックが行われる。 このとき、シグナルを送信するプロセスは F_SETOWN を使ったプロセスである (但し、下記の「バグ」の章を参照のこと)。
ファイルディスクリプタがソケットを参照している場合は、 F_SETOWN を使用して、ソケットに帯域外 (out-of-band) データが届いた時に SIGURG シグナルを配送する相手を選択することもできる (SIGURG が送られた場合には select(2) がソケットが「特別な状態」にあると報告することだろう)。
スレッドグループをサポートしているスレッドライブラリ (例えば NPTL) を 使って動作しているマルチスレッド・プロセスで F_SETSIG に 0 以外の値を指定した場合、 F_SETOWN に正の値を渡すと、その意味が違ってくる: プロセス全体を示すプロセスID ではなく、プロセス内の特定の スレッドを示すスレッドID と解釈される。 したがって、 F_SETSIG を使う場合には、きちんと結果を受け取るには、 F_SETOWN に渡す値を getpid(2) ではなく gettid(2) の返り値にする必要があるだろう。 (現状の Linux スレッド実装では、メイン・スレッドのスレッドID は そのスレッドのプロセスID と同じである。つまり、 シグナル・スレッドのプログラムではこの場合 gettid(2) と getpid(2) は全く同じように使うことができる。) ただし、注意すべき点として、この段落で述べたことは、 ソケットの帯域外データが届いたときに生成される SIGURG シグナルにはあてはまらない。 このシグナルは常にプロセスかプロセスグループに送られ、 送信先は F_SETOWN に渡された値にしたがって決める。 また、以下の点にも注意すること。 Linux では一つのプロセスに対してキューに入れられるリアルタイム シグナルの数に上限が設けられており (getrlimit(2) と signal(7) を参照)、この上限に達するとカーネルは SIGIO シグナルを配送する。この SIGIO シグナルは、指定されたスレッドではなくプロセス全体に送られる。
- F_GETSIG (void)
- 入力や出力が可能になった場合に送るシグナルを (関数の結果として) 返す。 値ゼロは SIGIO を送ることを意味する。 (SIGIO を含む) 他の値はいずれも、 SIGIO の代わりに送るシグナル番号を表す。 後者の場合、シグナルハンドラを SA_SIGINFO フラグ付きで設定すれば、ハンドラで追加の情報を得ることができる。 arg は無視される。
- F_SETSIG (long)
- 入力や出力が可能になった場合に送るシグナルを
arg
に指定された値に設定する。
値ゼロは SIGIO
を送ることを意味する。
(SIGIO を含む)
他の値はいずれも、
SIGIO
の代わりに送るシグナル番号を表す。
後者の場合、シグナルハンドラを
SA_SIGINFO
フラグ付きで設定すれば、
ハンドラで追加の情報を得ることができる。
また、 F_SETSIG に 0 以外の値を渡すと、シグナルの受信者をプロセス全体から プロセス内の特定のスレッドに変更される。詳細は F_SETOWN の説明を参照のこと。
F_SETSIG にゼロ以外の値を設定し、シグナルハンドラに SA_SIGINFO フラグを設定すると、 (sigaction(2) を参照) I/O イベントに関する追加の情報が siginfo_t 構造体でシグナルハンドラへ渡される。 si_code フィールドが示すシグナルの原因が SI_SIGIO である場合、 si_fd フィールドにはイベントに対応するファイルディスクリプタが入っている。 それ以外の場合は、どのファイルディスクリプタが利用可能かを示す情報は ないので、どのファイルディスクリプタで I/O が可能かを判断するためには 通常の機構 (select(2), poll(2), O_NONBLOCK を設定した read(2) など) を使用しなければならない。
リアルタイムシグナル (値が SIGRTMIN 以上) を選択している場合は、 同じシグナル番号を持つ複数の I/O イベントがキューに入ることがある (キューに入れるかどうかは利用可能なメモリに依存している)。 上記と同様、 SA_SIGINFO が設定されている場合、シグナルハンドラのための追加の情報が得られる。
これらの機構を使用することで、ほとんどの場合で select(2) や poll(2) を使用せずに完全な非同期 I/O を実装することができる。
O_ASYNC, F_GETOWN, F_SETOWN の使用は BSD と Linux に特有である。 F_GETSIG と F_SETSIG は Linux 固有である。POSIX には、同様のことを行うために、非同期 I/O と aio_sigevent 構造体がある。Linux では、GNU C ライブラリ (Glibc) の一部として これらも利用可能である。
リース (leases)¶
(Linix 2.4 以降で利用可能) F_SETLEASE は、 fd が参照するオープンファイル記述に対して新しいリースを設定するのに使用される。 F_GETLEASE は、 fd が参照するオープンファイル記述に対して設定されている 現在のリースを取得するのに使用される。 ファイルのリースにより、 あるプロセス ("lease breaker") がそのファイルディスクリプタが参照 しているファイルに対して open(2) や truncate(2) を行おうとした際に、リースを保持しているプロセス ("lease holder") へ (シグナルの配送による) 通知が行われるという機構が提供される。
- F_SETLEASE (long)
- arg の内容に基いてファイルのリースの設定、削除を行う。整数 arg には以下の値が指定できる:
- F_RDLCK
- 読み出しリースを取得する。これにより、 そのファイルが書き込み用にオープンされたり、ファイルが切り詰められた場合に、 呼び出し元のプロセスに通知が行われるようになる。 読み出しリースを設定できるのは、読み出し専用でオープンされている ファイルディスクリプタに対してのみである。
- F_WRLCK
- 書き込みリースを取得する。これにより、 (読み出し用か書き込み用にかかわらず) そのファイルがオープンされたり、 ファイルが切り詰められた場合に、呼び出し元のプロセスに通知が行われるようになる。 書き込みリースは、そのファイルに対するオープンされたファイルディスクリプタが 他にない場合にのみ設定できる。
- F_UNLCK
- そのファイルからリースを削除する。
リースはオープンファイル記述に対して関連付けられる (open(2) 参照)。 つまり、 (fork(2) や dup(2) などにより作成された) ファイルディスクリプタの複製は同じリースを参照し、 複製も含めたどのファイルディスクリプタを使ってもこのリースを変更したり 解放したりできる。 また、これらのファイルディスクリプタのいずれかに対して F_UNLCK 操作が明示的に実行された場合や、すべてのファイルディスクリプタが 閉じられた場合にも、リースは解放される。
リースの取得は通常のファイル (regular file) に対してのみ可能である。 非特権プロセスがリースを取得できるのは、UID (所有者) がプロセスの ファイルシステム UID と一致するファイルに対してだけである。 CAP_LEASE ケーパビリティを持つプロセスは任意のファイルに対してリースを取得できる。
- F_GETLEASE (void)
- ファイルディスクリプタ fd に対して設定されているリースの種別を取得する。 F_RDLCK, F_WRLCK, F_UNLCK のいずれかが返される。 F_RDLCK, F_WRLCK はそれぞれ、読み出しリース、書き込みリースが設定されていることを示し、 F_UNLCK はリースが何も設定されていないことを示す。 arg は無視される。
あるプロセス ("lease folder") が F_SETLEASE で設定されたリースと矛盾するような open(2) や truncate(2) を実行した場合、 そのシステムコールはカーネルによって停止され、 カーネルは lease holder にシグナル (デフォルトでは SIGIO) を送って通知を行う。 lease holder はこのシグナルを受信したときにはきちんと対応すべきである。 具体的には、別のプロセスがそのファイルにアクセスするための準備として 必要な後片付け (例えば、キャッシュされたバッファのフラッシュ) を すべて行ってから、そのファイルのリースの削除または格下げを行う。 リースを削除をするには、 arg に F_UNLCK を指定して F_SETLEASE を実行する。 lease holder がファイルに書き込みリースを保持していて、 lease breaker が読み出し用にそのファイルをオープンしている場合、 lease holder が保持しているリースを読み出しリースに格下げすれば 十分である。これをするには、 arg に F_RDLCK を指定して F_SETLEASE を実行する。
lease holder が /proc/sys/fs/lease-break-time で指定された秒数以内にリースの格下げか削除を行えなかった場合、 カーネルは強制的にその lease holder のリースを削除もしくは格下げを行う。
一度リースの削除か格下げが自発的もしくは強制的に行われると、 lease breaker がまだシステムコールを再開していない場合には、 カーネルが lease breaker のシステムコールの続行を許可する。
lease breaker が実行した open(2) や truncate(2) が停止中にシグナルハンドラにより中断された場合、 そのシステムコールは EINTR エラーで失敗するが、上で述べた他の処理は そのまま行われる。 open(2) や truncate(2) が停止中に lease breaker がシグナルにより kill された場合、 上で述べた他の処理はそのまま行われる。 lease breaker が open(2) を呼ぶ際に O_NONBLOCK フラグを指定した場合、そのシステムコールは EWOULDBLOCK エラーで直ちに失敗するが、上で述べた他の処理はそのまま行われる。
lease holder への通知に使われるデフォルトのシグナルは SIGIO だが、 fcntl() の F_SETSIG コマンドで変更することができる。 F_SETSIG コマンドが実行され (SIGIO を指定された場合も含む)、 SA_SIGINFO フラグ付きでシグナルハンドラが設定されている場合には、 ハンドラの第二引き数として siginfo_t 構造体が渡され、この引き数の si_fd フィールドには別のプロセスがアクセスしたリース設定済みファイルの ディスクリプタが入っている (この機能は複数のファイルに対してリースを設定する場合に有用である)。
ファイルやディレクトリの変更の通知 (dnotify)¶
- F_NOTIFY (long)
- (Linux 2.4 以降) fd で参照されるディレクトリか、その中にあるファイルに変更があった場合に 通知を行う。どのイベントを通知するかは arg で指定する。 arg はビットマスクで、以下のビットの 0個以上の論理和をとったものを指定する。
- DN_ACCESS
- ファイルへのアクセスがあった (read, pread, readv)
- DN_MODIFY
- ファイルの内容が変更された (write, pwrite, writev, truncate, ftruncate).
- DN_CREATE
- ファイルが作成された (open, creat, mknod, mkdir, link, symlink, rename).
- DN_DELETE
- ファイルが削除 (unlink) された (unlink, 別のディレクトリへの rename, rmdir)
- DN_RENAME
- ディレクトリ内でのファイル名の変更があった (rename)
- DN_ATTRIB
- ファイル属性が変更された (chown, chmod, utime[s])
- (上記の定義を利用するには
_GNU_SOURCE
機能検査マクロを定義しなければならない。)
ディレクトリの変更通知は通常「一回限り (one-shot)」であり、 アプリケーション側でその後さらに通知を受信したい場合は 再登録しなければならない。 arg に DN_MULTISHOT が含まれていた場合には、 変更通知は明示的に解除されるまで有効状態が継続する。
F_NOTIFY 要求は積算されていく。つまり、 arg で指定されたイベントがすでにモニタされている イベント集合に加算される形になる。 すべてのイベントの通知を無効にするには、 arg に 0 を指定して F_NOTIFY を呼び出す必要がある。
通知はシグナルの配送で行われる。 デフォルトのシグナルは SIGIO だが、 fcntl() の F_SETSIG コマンドで変更することができる。 後者の場合には、 (SA_SIGINFO フラグ付きでシグナルハンドラが設定されている場合には) ハンドラの第二引き数として siginfo_t 構造体が渡され、この構造体の si_fd フィールドには通知の行われたファイルディスクリプタが入っている (この機能は複数のディレクトリに対して通知を設定する場合に有用である)。
特に DN_MULTISHOT を使う場合は、通知にはリアルタイムシグナルを使うべきである。 それは、リアルタイムシグナルを使うことで、複数の通知をキューに入れる ことができるからである。
注意: 新しくアプリケーションを書く際には、(カーネル 2.6.13 以降で利用可能となった) inotify インタフェースを使用すべきである。 inotify はファイルシステムイベントの通知を取得するための ずっと優れたインタフェースである。 inotify(7) を参照。
返り値¶
成功した場合の返り値は操作の種類により違う:
- F_DUPFD
- 新しいディスクリプタを返す。
- F_GETFD
- フラグの値を返す。
- F_GETFL
- フラグの値を返す。
- F_GETLEASE
- ファイルディスクリプタに対して保持されているリースの種別を返す。
- F_GETOWN
- ディスクリプタの所有者を返す。
- F_GETSIG
- 読み込みや書き出しが可能になった時に送られるシグナルの値、もしくは 伝統的な SIGIO 動作の場合にはゼロを返す。
- 他の全てのコマンドは 0 を返す。
エラーの時は -1 が返され、 errno に適切な値が設定される。
エラー¶
- EACCES か EAGAIN
- 他のプロセスが保持しているロックによって操作が禁止されている。
- EAGAIN
- そのファイルは他のプロセスによってメモリ・マップされているため、 操作が禁止されている。
- EBADF
- fd がオープンされたファイルディスクリプタでない。 あるいはコマンドが F_SETLK または F_SETLKW だったが、対象のファイルディスクリプタのオープンモードが 必要となるロックの型にマッチしていない。
- EDEADLK
- 指定された F_SETLKW コマンドを実行した場合にはデッドロックになることが検出された。
- EFAULT
- lock が利用可能なアドレス空間の外部にある。
- EINTR
- F_SETLKW コマンドがシグナルにより割り込まれた (signal(7) 参照)。 F_GETLK と F_SETLK の場合、ロックを確認したり取得したりする前にシグナルによって割り込まれた。 これはたいていリモートのファイルをロックする場合 (例えば NFS 上でロックする場合) に起こる。 しかしローカルでも起こる場合がある。
- EINVAL
- F_DUPFDで、 arg が負か、もしくは許される最大値よりも大きい。 F_SETSIG の場合、 arg が利用可能なシグナル番号ではない。
- EMFILE
- F_DUPFDで、 プロセスがすでに最大数までファイルディスクリプタをオープンしている。
- ENOLCK
- オープンされているロックの数が多過ぎて、ロック・テーブルがいっぱいである。 または remote locking protocol (例えば NFS 上のロック) が失敗した。
- EPERM
- 追加専用属性が設定されたファイルの O_APPEND フラグをクリアしようと試みた。
準拠¶
SVr4, 4.3BSD, POSIX.1-2001. POSIX.1-2001 で規定されている操作は、 F_DUPFD, F_GETFD, F_SETFD, F_GETFL, F_SETFL, F_GETLK, F_SETLK, F_SETLKW, F_GETOWN, F_SETOWN だけである。
F_DUPFD_CLOEXEC は POSIX.1-2008 で規定されている。
F_GETSIG, F_SETSIG, F_NOTIFY, F_GETLEASE, F_SETLEASE は Linux 固有である (これらの定義を有効にするには _GNU_SOURCE マクロを定義すること)。
注意¶
エラーの際の返り値が dup2(2) と F_DUPFD では異なっている。
カーネル 2.0 以降では、 flock(2) と fcntl() が設定するロック種別の間に相互作用はない。
システムによっては、 struct flock に上記以外のフィールドがあるものもある (例えば l_sysid)。 はっきりと言えることは、ロックを保持しているプロセスが別のマシンに存在 する場合には、 l_pid だけはあまり役にたたないだろうということである。
バグ¶
いくつかのアーキテクチャ (特に i386) における Linux システムコールの慣習 のため以下の制限が存在する。 F_GETOWN が返す (負の) プロセスグループID が -1 から -4095 の範囲に入った場合、 glibc はこの返り値をシステムコールでエラーが起こったと 間違って解釈してしまう。つまり、 fcntl() の返り値は -1 となり、 errno には (正の) プロセスグループID が設定されることになる。
Linux 2.4 以前では、非特権プロセスが F_SETOWN を使って、ソケットのファイルディスクリプタの所有者に 呼び出し元以外のプロセス (やプロセスグループ) を指定すると 発生するバグがある。この場合、 呼び出し元が所有者として指定したプロセス (やプロセスグループ) に シグナルを送る許可を持っていたとしても、 fcntl() が -1 を返し errno に EPERM を設定することがある。 このエラーが返ったにもかかわらず、ファイルディスクリプタの所有者 は設定され、シグナルはその所有者に送られる。
これまでの Linux の全てのバージョンにおける強制ロックの実装は、 競合条件下で強制ロックが不完全になるような場合がある。 ロックと重なって実行された write(2) の呼び出しは強制ロックが獲得された後にもデータを変更することができる。 ロックと重なって実行された read(2) の呼び出しは強制ロックが獲得された後になって行われたデータの変更を 検出することができる。 同様の競合条件が強制ロックと mmap(2) の間にも存在する。それゆえ、強制ロックに頼るのはお薦めできない。
関連項目¶
dup2(2), flock(2), open(2), socket(2), lockf(3), capabilities(7), feature_test_macros(7)
カーネルソース内の Documentation/locks.txt, Documentation/mandatory.txt, Documentation/dnotify.txt も参照のこと。
2009-02-23 | Linux |